package client.zookeeper;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.ACL;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONValidator;

import client.zookeeper.IZookeeperClient.ConnectInfo;
import client.zookeeper.impl.DO.ZookeeperPathTreeNode;
import client.zookeeper.impl.DO.ZookeeperRootTreeNode;
import core.AlertUtil;
import core.FormDialog;
import core.TextAreaAppender;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class ZookeeperClientController implements Initializable{

	//应用日志对象
	private Logger appLogger;

	/**
	 * Zookeeper客户端名称与实例的Map集合
	 */
	private static final Map<String,IZookeeperClient> ZOOKEEPER_CLIENT=new HashMap<String,IZookeeperClient>();
	static {
		//通过服务发现查找Zookeeper客户端实例
		ServiceLoader<IZookeeperClient> clients=ServiceLoader.load(IZookeeperClient.class);
		for(IZookeeperClient client:clients) {
			ZOOKEEPER_CLIENT.put(client.getName(), client);
		}
	}

	@FXML
	private BorderPane container;

	/**
	 * 左侧树结构
	 */
	@FXML
	private TreeView<ZookeeperTreeNode> zkTreeView;

	/**
	 * 信息组件
	 */
	@FXML
	private TextArea infoTextArea;

	/**
	 * 命令历史组件
	 */
	@FXML
	private TextArea cmdTextArea;

	/**
	 * 命令输入框
	 */
	@FXML
	private TextField cmdTextField;

	/**
	 * 日志组件
	 */
	@FXML
	private TextArea logTextArea;

	/**
	 * zookeeper客户端连接实例
	 */
	private IZookeeperClient zookeeperClient;

	@Override
	public void initialize(URL location, ResourceBundle resources) {
		appLogger = Logger.getLogger("zookeeper_client_app");
		appLogger.addAppender(new TextAreaAppender(logTextArea));
		appLogger.info("日志输出初始化完成！");

		Platform.runLater(()->{
			Stage stage = (Stage)container.getScene().getWindow();
			 stage.setOnCloseRequest((event)->{
				 if(zookeeperClient!=null){
					 try {
						 zookeeperClient.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				 }
			 });
		});
	}

	/**
	 * 选择zookeeper客户端。
	 * 如果只有一个实例则直接使用，多个时弹出选择对话框进行选择。
	 * @return
	 */
	private IZookeeperClient choiceClient() {
		appLogger.info("进行客户端选择。");
		if(ZOOKEEPER_CLIENT.size() == 1) {
			appLogger.info("仅有一个客户端示例，无需选择！");
			//只有一种客户端
			for(IZookeeperClient client:ZOOKEEPER_CLIENT.values()) {
				return client;
			}
		}

		appLogger.info("启动客户端选择对话框。");
		//弹出选择对话框进行选择
		Set<String> clients = ZOOKEEPER_CLIENT.keySet();
		Optional<String> result = AlertUtil.choice("选择客户端", "请选择连接客户端类型：", clients);
		if(result.isPresent()) {
			appLogger.info("用户选择客户端："+result.get());
			return ZOOKEEPER_CLIENT.get(result.get());
		}

		return null;
	}

	/**
	 * 新建连接按钮监听
	 * @param e	事件对象
	 * @throws IOException　zookeeper连接操作可能发生异常
	 */
	public void createConnection(ActionEvent e) throws IOException{
		appLogger.info("检查是否存在已连接的客户端。");
		//如果zookeeper客户端实例不为空则先关闭之前的连接
		if(zookeeperClient != null) {
			appLogger.info("关闭已连接的客户端。");
			zookeeperClient.close();
		}

		//选择zookeeper客户端
		this.zookeeperClient = this.choiceClient();
		if(zookeeperClient != null) {
			//初始化zookeeper连接和组件
			boolean connected=zookeeperClient.init((data)->{
				cmdTextArea.appendText(data);
			});
			if(connected){
				initTreeView();
			}
		}
	}

	/**
	 * 初始化TreeView树
	 */
	private void initTreeView() {
		appLogger.info("初始化Zookeeper树结构。");
		TreeItem<ZookeeperTreeNode> root = this.getRootTreeItem();
		zkTreeView.setRoot(root);

		appLogger.info("添加节点选择监听事件！");
		//节点选择事件处理
		zkTreeView.getSelectionModel().selectedItemProperty().addListener((observable,oldValue,newValue)->{
			appLogger.info("Zookeeper树节点被点击 -> oldValue:"+oldValue+",newValue:"+newValue);
			TreeItem<ZookeeperTreeNode> currentSelectedItem = (TreeItem<ZookeeperTreeNode>) newValue;
			if(currentSelectedItem==null) return;

			//设置节点信息
			ZookeeperTreeNode selectedNode = currentSelectedItem.getValue();
			String itemInfo = this.getItemInfo(selectedNode);
			infoTextArea.setText(itemInfo);

			//如果是DB节点则刷新子节点
			if(!selectedNode.isLeaf() && !selectedNode.isLoaded()) {
				currentSelectedItem.getChildren().clear();
				List<TreeItem<ZookeeperTreeNode>> childNodeList = this.getChildren(selectedNode);
				currentSelectedItem.getChildren().addAll(childNodeList);
				currentSelectedItem.setExpanded(true);
			}

			selectedNode.setLoaded(true);
		});

		appLogger.info("设置树节点右键菜单！");
		//设置TreeView树邮件菜单
		MenuItem refreshItem = new MenuItem("刷新");
		refreshItem.setOnAction((event)->{
			appLogger.info("执行[刷新]动作！");
			TreeItem<ZookeeperTreeNode> selectedItem = zkTreeView.getSelectionModel().getSelectedItem();
			ZookeeperTreeNode zkTreeNode = selectedItem.getValue();
			//刷新信息
			infoTextArea.setText(this.getItemInfo(zkTreeNode));

			//刷新子节点
			if(!zkTreeNode.isLeaf()) {
				selectedItem.getChildren().clear();
				selectedItem.getChildren().addAll(this.getChildren(zkTreeNode));
				selectedItem.setExpanded(true);
			}
		});

		MenuItem disconnectItem = new MenuItem("断开连接");
		disconnectItem.setOnAction((event)->{
			appLogger.info("执行[断开连接]动作！");
			try {
				zookeeperClient.close();
				zkTreeView.setRoot(null);
				zkTreeView.setContextMenu(null);
				infoTextArea.clear();
				cmdTextArea.setText("连接已断开！\n");
			} catch (IOException e) {
				AlertUtil.exception(e);
			}

		});

		MenuItem createPathItem = new MenuItem("创建");
		createPathItem.setOnAction((event)->{
			appLogger.info("执行[创建]动作！");
			TreeItem<ZookeeperTreeNode> selectedItem = zkTreeView.getSelectionModel().getSelectedItem();
			ZookeeperTreeNode zkTreeNode = selectedItem.getValue();
			if(zkTreeNode instanceof ZookeeperPathTreeNode) {
				String currPath = ((ZookeeperPathTreeNode) zkTreeNode).getPath();
				Map<String,CreateMode> createModeMap=new HashMap<String,CreateMode>();
				createModeMap.put("持久化节点", CreateMode.PERSISTENT);
				createModeMap.put("持久化有序节点", CreateMode.PERSISTENT_SEQUENTIAL);
				createModeMap.put("临时节点", CreateMode.EPHEMERAL);
				createModeMap.put("临时有序节点", CreateMode.EPHEMERAL_SEQUENTIAL);
				createModeMap.put("容器节点", CreateMode.CONTAINER);
				createModeMap.put("持久化节点(带有效期)", CreateMode.PERSISTENT_WITH_TTL);
				createModeMap.put("持久化有序节点(带有效期)", CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL);
				ComboBox<String> typeComboBox = new ComboBox<String>(
						FXCollections.observableArrayList(createModeMap.keySet()));
				typeComboBox.getSelectionModel().select("持久化节点"); //默认选项
				TextField nameTextFiled =new TextField();
				FormDialog createNodeDialog = new FormDialog("新建节点",
						new FormDialog.Item("类型：",typeComboBox),
						new FormDialog.Item("名称",nameTextFiled));
				Optional<Boolean> result = createNodeDialog.showAndWait();
				if(result.isPresent()) {
					String type = typeComboBox.getValue();
					String name = nameTextFiled.getText();
					String path = currPath.endsWith("/")?currPath+name:currPath+"/"+name;
					try {
						zookeeperClient.create(path, createModeMap.get(type));

						//刷新子节点
						selectedItem.getChildren().clear();
						List<TreeItem<ZookeeperTreeNode>> childNodeList = this.getChildren(zkTreeNode);
						selectedItem.getChildren().addAll(childNodeList);
						selectedItem.setExpanded(true);
					} catch (Exception e) {
						AlertUtil.exception("创建失败", e);
					}
				}
			}
		});

		MenuItem delPathItem = new MenuItem("删除");
		delPathItem.setOnAction((event)->{
			appLogger.info("执行[删除]动作！");
			TreeItem<ZookeeperTreeNode> selectedItem = zkTreeView.getSelectionModel().getSelectedItem();
			ZookeeperTreeNode zkTreeNode = selectedItem.getValue();
			if(zkTreeNode instanceof ZookeeperPathTreeNode) {
				String path = ((ZookeeperPathTreeNode) zkTreeNode).getPath();
				try {
					zookeeperClient.delete(path);
					selectedItem.getParent().getChildren().clear(); //清理当前层级节点，重新加载
				} catch (Exception e) {
					AlertUtil.exception(e);
				}
			}
		});

		zkTreeView.setContextMenu(new ContextMenu(disconnectItem));
		zkTreeView.setOnContextMenuRequested((event)->{
			zkTreeView.getContextMenu().getItems().clear();
			TreeItem<ZookeeperTreeNode> selectedItem = zkTreeView.getSelectionModel().getSelectedItem();
			ZookeeperTreeNode zkTreeNode = selectedItem.getValue();
			if(zkTreeNode instanceof ZookeeperRootTreeNode) {
				zkTreeView.getContextMenu().getItems().addAll(refreshItem,disconnectItem);
			}else if(zkTreeNode instanceof ZookeeperPathTreeNode) {
				zkTreeView.getContextMenu().getItems().addAll(refreshItem,createPathItem,delPathItem);
			}
		});

		appLogger.info("Zookeeper树结构初始化完成！");
	}

	/**
	 * 获取根节点
	 * @return　根节点对象
	 */
	private TreeItem<ZookeeperTreeNode> getRootTreeItem() {
		appLogger.info("获取Zookeeper树根节点！");
		ConnectInfo connectInfo=zookeeperClient.getConnectInfo();
		ZookeeperRootTreeNode rootNode = new ZookeeperRootTreeNode(connectInfo.getConnStr());
		TreeItem<ZookeeperTreeNode> rootItem = new TreeItem<ZookeeperTreeNode>(rootNode);
		rootItem.setExpanded(true);
		rootItem.getChildren().addAll(this.getChildren(rootNode));
		return rootItem;
	}

	/**
	 * 获取子节点集合
	 * @param selectedNode　当前选中节点
	 * @return　子节点集合
	 */
	private List<TreeItem<ZookeeperTreeNode>> getChildren(ZookeeperTreeNode selectedNode) {
		appLogger.info("获取Zookeeper树子节点！当前节点："+selectedNode);
		if(selectedNode instanceof ZookeeperRootTreeNode) {
			List<TreeItem<ZookeeperTreeNode>> childrenList = new ArrayList<>();
			ZookeeperPathTreeNode pathNode = new ZookeeperPathTreeNode("/","/");
			TreeItem<ZookeeperTreeNode> pathItem = new TreeItem<ZookeeperTreeNode>(pathNode);
			childrenList.add(pathItem);
			return childrenList;
		}else if(selectedNode instanceof ZookeeperPathTreeNode) {
			ZookeeperPathTreeNode currPathNode = (ZookeeperPathTreeNode)selectedNode;
			String currPath = currPathNode.getPath();
			List<TreeItem<ZookeeperTreeNode>> childrenList = new ArrayList<>();
			try {
				List<String> children = zookeeperClient.getChildren(currPath);
				for(String name:children) {
					String path = currPath.endsWith("/")?currPath+name:currPath+"/"+name;
					ZookeeperPathTreeNode pathNode = new ZookeeperPathTreeNode(path,name);
					TreeItem<ZookeeperTreeNode> pathItem = new TreeItem<ZookeeperTreeNode>(pathNode);
					childrenList.add(pathItem);
				}
			} catch (Exception e) {
				AlertUtil.exception("获取子节点异常", e);
			}
			return childrenList;
		}
		return null;
	}

	/**
	 * 获取节点信息
	 * @param selectedNode　当前选中节点
	 * @return　节点信息
	 */
	private String getItemInfo(ZookeeperTreeNode selectedNode) {
		appLogger.info("获取Zookeeper树节点详情信息。当前节点："+selectedNode);
		if(selectedNode instanceof ZookeeperRootTreeNode) {
			return "";
		}else if(selectedNode instanceof ZookeeperPathTreeNode) {
			String path = ((ZookeeperPathTreeNode) selectedNode).getPath();
			try {
				StringBuilder strBuilder = new StringBuilder();
				List<ACL> aclList = zookeeperClient.getACL(path);
				for(ACL acl:aclList) {
					strBuilder.append(acl.toString()).append("\n");
				}
				strBuilder.append("------------------------------------------------------------\n");
				byte[] data=zookeeperClient.getData(path);
				if(data == null) {
					return "";
				}
				String nodeData = new String(data,StandardCharsets.UTF_8);
				try {
					if(JSONValidator.from(nodeData).validate()) {
						//对json进行格式化输出
						nodeData=JSON.toJSONString(JSON.parse(nodeData),true);
					}
				} catch (Exception e) {
					//非json字符串
				}
				strBuilder.append(nodeData);
				return strBuilder.toString();
			} catch (Exception e) {
				AlertUtil.exception(e);
			}
		}
		return null;
	}

	/**
	 * 执行命令动作监听
	 * @param e　事件对象
	 */
	public void exeCmd(ActionEvent e) {
		if(zookeeperClient == null) {
			appLogger.error("无法执行用户输入命令！【未建立连接】");
			return;
		}

		String cmdStr = cmdTextField.getText();
		if(cmdStr == null || "".equals(cmdStr.trim())) {
			appLogger.error("无法执行用户输入命令！【命令为空】");
			return;
		}

		appLogger.info("执行用户输入命令："+cmdStr);
		//执行zookeeper命令
		String result = zookeeperClient.exeCmd(cmdStr);
		//清空命令输入框
		cmdTextField.clear();
		//将命令和响应追加到命令执行历史内
		cmdTextArea.appendText("> "+cmdStr+"\n");
		if(result!=null) {
			cmdTextArea.appendText(result+"\n");
		}
	}
}
